package com.txtsqlclient.dataaccess;

import java.lang.reflect.InvocationTargetException;
import java.sql.Connection;
import java.sql.SQLException;

import com.txtsqlclient.dataaccess.database.QueryExecutionDAO;
import com.txtsqlclient.dataaccess.database.connection.ConnectionDetailsVO;
import com.txtsqlclient.dataaccess.database.connection.ConnectionException;
import com.txtsqlclient.dataaccess.database.connection.ConnectionFactory;

/**
 * This class is a singleton factory to create DAOs.
 * 
 * @author spadda
 */
public class DAOFactory {

    private static DAOFactory factory = null;

    private Connection dbCon = null;

    public DAOFactory(ConnectionDetailsVO connectionDetails)
            throws DAOException {
        ConnectionFactory conFactory = ConnectionFactory.getInstance();
        try {
            this.dbCon = conFactory.openConnection(connectionDetails);
        } catch (ConnectionException ce) {
            throw new DAOException(
                    "Cannot create the DAO factory due to an error while opening the database connection",
                    ce);
        }
    }

    /**
     * This method is required to get an instance of this factory.
     * 
     * @param connectionDetails
     * 
     * @return the singleton instance of this factory.
     * @throws DAOException
     */
    public static DAOFactory getInstance(ConnectionDetailsVO connectionDetails)
            throws DAOException {
        if (factory == null) {
            factory = new DAOFactory(connectionDetails);
        }
        try {
            if (factory.dbCon.isClosed()) {
                ConnectionFactory conFactory = ConnectionFactory.getInstance();
                factory.dbCon = conFactory.openConnection(connectionDetails);
            }
        } catch (ConnectionException ce) {
            throw new DAOException(
                    "Cannot create the DAO factory due to an error while opening the database connection",
                    ce);
        } catch (SQLException sqle) {
            throw new DAOException(
                    "Cannot create the DAO factory due to an error while opening the database connection",
                    sqle);
        }
        return factory;
    }

    /**
     * This method is required to get the DAO.
     * 
     * @param type
     * @param connectionDetails
     * @return a DAO for the specified <code>DAOType</code>
     * @throws DAOCreationException
     */
    public QueryExecutionDAO getDAO(DAOType type) throws DAOCreationException {
        Class c = null;
        String className = type.getDAOClassName();
        QueryExecutionDAO dao = null;
        try {
            c = Class.forName(className);
            dao = (QueryExecutionDAO) c.getConstructor(new Class[] { Connection.class })
                    .newInstance(new Object[] { this.dbCon });
        } catch (ClassNotFoundException cnfe) {
            throw new DAOCreationException("Invalid DAO class name:" + className, cnfe);
        } catch (IllegalAccessException iae) {
            throw new DAOCreationException("Could not access constructor of the DAO class:"
                    + className, iae);
        } catch (InstantiationException ie) {
            throw new DAOCreationException("Error creating an instance of the DAO class:"
                    + className, ie);
        } catch (InvocationTargetException ite) {
            throw new DAOCreationException("Error creating an instance of the DAO class:"
                    + className, ite);
        } catch (NoSuchMethodException nsme) {
            throw new DAOCreationException("Error creating an instance of the DAO class:"
                    + className, nsme);
        }
        return dao;
    }

    /**
     * This method is required to clean up the database
     * 
     * @throws DAOException
     */
    public static void cleanUp() throws DAOException {
        if (factory == null) {
            return;
        }
        factory.closeConnection();
        factory = null;
    }

    public void closeConnection() throws DAOException {
        if (this.dbCon == null) {
            return;
        }
        try {
            ConnectionFactory.getInstance().closeConnection(this.dbCon);
        } catch (ConnectionException e) {
            throw new DAOException("Error while closing connection", e);
        }
    }
}